package com.zxcz.aliyun;

import java.net.URL;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse.Credentials;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class OssManager {
	private static final long TIMEOUT = 3600;//签名过期时长，单位为秒
    private static final long STS_TIMEOUT = 60*60;//sts过期时长，单位为秒,最小15分钟，最大一个小时
    private static final String STS_END_POINT = "sts.aliyuncs.com";
    private static final String SESSION_NAME = "roleUser";
    private static final Log logger = LogFactory.getLog(OssManager.class);
    
	@Autowired
	private OssConfig ossConfig;
	@Autowired
	private RedissonClient redisson;
	
	/**
	 * 生成临时安全令牌（Security Token Service，STS）
	 * @param roleArn 角色arn,角色的资源名称（Aliyun Resource Name，简称ARN）
	 * @param durationSeconds 超时时间
	 * @return STS
	 */
	private Credentials generateSts(String roleArn, long durationSeconds) {
		//构造default profile
		IClientProfile profile = DefaultProfile.getProfile("", ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
		// 用profile构造client
		DefaultAcsClient client = new DefaultAcsClient(profile);
		
		final AssumeRoleRequest request = new AssumeRoleRequest();
		request.setSysMethod(MethodType.POST);
		request.setSysEndpoint(STS_END_POINT);
		request.setRoleArn(roleArn);
		request.setRoleSessionName(SESSION_NAME);
		// request.setPolicy(null); // 若policy为空，则用户将获得该角色下所有权限
		request.setDurationSeconds(durationSeconds); // 设置凭证有效时间
		
		AssumeRoleResponse response = null;
		try {
			// 发起请求，并得到response
			response = client.getAcsResponse(request);
			return response.getCredentials(); 
		} catch (ClientException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	/**
	 * 获取只读权限的sts
     * @return sts
	 */
	private Credentials getReadSts() {
		Credentials credentials = null;
		RBucket<Object> bucket = redisson.getBucket("readSTS");
		long remainTimeToLive = bucket.remainTimeToLive(); // 缓存key过期时间，单位毫秒
		long expirationTime = remainTimeToLive / 1000; // 将毫秒转为秒
		RLock rLock = redisson.getLock("readStsLock");
		
		try {
			// 尝试加锁，最多等待10秒，上锁以后5秒自动解锁
			boolean res = rLock.tryLock(10, 5, TimeUnit.SECONDS);
			if (res) {
				//判断缓存是否存在，或者缓过期时间少于指定时间(5分钟)
				if (!bucket.isExists() || expirationTime <= 60*5) {
					credentials = generateSts(ossConfig.getDownloadARN(),STS_TIMEOUT);
					//缓存
					ObjectMapper objectMapper = new ObjectMapper();
					String stsString = objectMapper.writeValueAsString(credentials);
					bucket.set(stsString);
					//设置缓存过期时间为1小时
					bucket.expire(60*60, TimeUnit.SECONDS);
				} else {
					//从缓存获取
					String stsString = (String) bucket.get();
					ObjectMapper objectMapper = new ObjectMapper();
					credentials = objectMapper.readValue(stsString, Credentials.class);
				}
			} 
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			rLock.unlock();
		}
		
		return credentials;
	}
	
	/**
	 * 获取写权限的sts
     * @return sts
	 */
	private Credentials getWriteSts() {
		Credentials credentials = null;
		RBucket<Object> bucket = redisson.getBucket("writeSTS");
		long remainTimeToLive = bucket.remainTimeToLive(); // 缓存key过期时间，单位毫秒
		long expirationTime = remainTimeToLive / 1000; // 将毫秒转为秒
		RLock rLock = redisson.getLock("writeStsLock");
		
		try {
			// 尝试加锁，最多等待10秒，上锁以后5秒自动解锁
			boolean res = rLock.tryLock(10, 5, TimeUnit.SECONDS);
			if (res) {
				//判断缓存是否存在，或者缓存过期时间少于指定时间(5分钟)
				if (!bucket.isExists() || expirationTime < 60*5) {
					credentials = generateSts(ossConfig.getUploadARN(),STS_TIMEOUT);
					//缓存
					ObjectMapper objectMapper = new ObjectMapper();
					String stsString = objectMapper.writeValueAsString(credentials);
					bucket.set(stsString);
					//设置缓存过期时间为1小时
					bucket.expire(60*60, TimeUnit.SECONDS);
					logger.info("请求阿里云获取sts令牌");
				} else {
					//从缓存获取
					String stsString = (String) bucket.get();
					ObjectMapper objectMapper = new ObjectMapper();
					credentials = objectMapper.readValue(stsString, Credentials.class);
					logger.info("从缓存获取sts令牌");
				}
			} 
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			rLock.unlock();
		}
		
		return credentials;
	}
	
	/**
	 * 生成签名过的地址
	 * @param credentials sts
     * @param objectName oss对象名称
     * @param isUpload 是否生成上传用的sts，true-是，false-否
     * @param seconds 资源访问的失效时长（单位：秒）
     * @return 签名url
	 */
	private String generateSignedUrl(Credentials credentials, String objectName, boolean isUpload, long seconds) {
		// 创建OSSClient实例
		OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndpoint(), 
				credentials.getAccessKeyId(), credentials.getAccessKeySecret(),credentials.getSecurityToken());
		//设置URL过期时间
		Date date = new Date(new Date().getTime() + seconds * 1000);
		//生成签名url
		URL url = null;
		if(isUpload) {
			GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(ossConfig.getBucketName(), objectName, HttpMethod.PUT);
			//DEFAULT_OBJECT_CONTENT_TYPE
			urlRequest.setContentType("application/octet-stream");
			urlRequest.setExpiration(date);
			url = ossClient.generatePresignedUrl(urlRequest);
		}else {
			url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), objectName, date);
		}
		//关闭ossClient
		ossClient.shutdown();
		
		return url.toString();
	}
	
	/**
	 * 获取下载用的已签名url
     * @param objectName oss对象名称
     * @return url
     */
	public String getDownloadUrl(String objectName) {
		Credentials readSts = getReadSts();
		if(readSts != null) {
			return generateSignedUrl(readSts,objectName, false, TIMEOUT);
		}
		return null;
	}
	
    /**
     * 获取上传用的已签名url
     * @param objectName oss对象名称
     * @return url
     */
	public String getUploadUrl(String objectName) {
		Credentials writeSts = getWriteSts();
		if(writeSts != null) {
			return generateSignedUrl(writeSts,objectName, true, TIMEOUT);
		}
		return null;
	}
	
	/**
     * 删除oss上指定的objectName
     * @param objectName oss对象名称
     */
	public void deleteObject(String objectName) {
		Credentials credentials = getWriteSts();
		// 创建OSSClient实例
		OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndpoint(), 
				credentials.getAccessKeyId(), credentials.getAccessKeySecret(),credentials.getSecurityToken());
		ossClient.deleteObject(ossConfig.getBucketName(), objectName);
	}
	
}
